package e2e

import (
	"encoding/json"
	"fmt"
	"io"
	"os"
	"path"
	"runtime"
	"testing"

	"github.com/lab-online/pkg/auth"
	"github.com/lab-online/pkg/logger"
	"github.com/magiconair/properties/assert"
)

type Case[T any] struct {
	Title   string `json:"title"`
	Skip    bool   `json:"skip"`
	Request struct {
		Auth *struct {
			Username string    `json:"username"`
			Role     auth.Role `json:"role"`
		} `json:"auth"`
		Body T `json:"body"`
	} `json:"request"`
	Response struct {
		Status int `json:"status"`
		Body   *struct {
			Code   int `json:"code"`
			Assert *[]struct {
				Key   any    `json:"key"`
				Func  string `json:"func"`
				Value any    `json:"value"`
			} `json:"data"`
		} `json:"body"`
	} `json:"response"`
}

type E2ESchema[T any] struct {
	Title             string    `json:"title"`
	Path              string    `json:"path"`
	Method            string    `json:"method"`
	ArgumentsValidate []Case[T] `json:"argumentsValidate"`
	BusinessValidate  []Case[T] `json:"businessValidate"`
}

func ReadTestJSONFile[T any](filepath string) *E2ESchema[T] {
	logger.Warn(filepath)
	jsonFile, err := os.Open(filepath)
	if err != nil {
		fmt.Println(err)
	}
	defer jsonFile.Close()

	byteValue, _ := io.ReadAll(jsonFile)

	var testdata E2ESchema[T]
	json.Unmarshal(byteValue, &testdata)

	return &testdata
}

func ParseRelativeFilePath(relativePath string) string {
	_, file, _, ok := runtime.Caller(1)
	logger.Warn(file)

	if !ok {
		panic("No caller information")
	}

	return path.Join(path.Dir(file), relativePath)
}

func DefineArgumentsValidateDescribe[T any](
	ctx *Context,
	testdata *E2ESchema[T],
) (string, func(t *testing.T)) {
	return testdata.Title, func(t *testing.T) {
		for _, e2eCase := range testdata.ArgumentsValidate {
			t.Run(e2eCase.Title, func(t *testing.T) {
				if e2eCase.Skip {
					t.Skip()
				}

				header := make(map[string]string)
				if e2eCase.Request.Auth != nil {
					header["Authorization"] = ctx.GenBearerToken(
						e2eCase.Request.Auth.Username,
						e2eCase.Request.Auth.Role,
					)
				}

				response := ctx.JSONRequest(&Request{
					Method: testdata.Method,
					Path:   ctx.Prefix + testdata.Path,
					Body:   e2eCase.Request.Body,
					Header: header,
				})

				assert.Equal(t, e2eCase.Response.Status, response.Code)
			})
		}
	}
}
